﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Xml.Linq;
using DynamicGeometry;

namespace SilverlightDG
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            ExpressionCompiler.Singleton = new DLR();
            PageSettings = new Settings(this);
            InitializeToolbar();
            canvas1.SizeChanged += canvas1_SizeChanged;
            DownloadDemoFile();
        }

        void canvas1_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (CurrentDrawing == null)
            {
                CurrentDrawing = new Drawing(canvas1);
                canvas1.SizeChanged -= canvas1_SizeChanged;
            }
        }

        #region Demo

        private void DownloadDemoFile()
        {
            WebClient internet = new WebClient();
            internet.DownloadStringCompleted += new DownloadStringCompletedEventHandler(internet_DownloadStringCompleted);
            internet.DownloadStringAsync(new Uri("http://www.osenkov.com/geometry/demo/Demo.xml"));
            ViewDemoButton.IsEnabled = false;
        }

        void internet_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Cancelled || e.Error != null)
            {
                return;
            }
            demoText = e.Result;
            ViewDemoButton.IsEnabled = true;
        }

        string demoText;

        private void ViewDemoButton_Click(object sender, RoutedEventArgs e)
        {
            LoadDrawing(demoText);
        }

        #endregion

        #region Drawing

        Drawing mCurrentDrawing;
        Drawing CurrentDrawing
        {
            get
            {
                return mCurrentDrawing;
            }
            set
            {
                if (mCurrentDrawing != null)
                {
                    mCurrentDrawing.ConstructionStepComplete -= mCurrentDrawing_ConstructionStepComplete;
                    mCurrentDrawing.ActionManager.CollectionChanged -= ActionManager_CollectionChanged;
                    mCurrentDrawing.Status -= mCurrentDrawing_Status;
                    mCurrentDrawing.SelectionChanged -= mCurrentDrawing_SelectionChanged;
                    mCurrentDrawing.BehaviorChanged -= mCurrentDrawing_BehaviorChanged;
                    mCurrentDrawing.DocumentOpenRequested -= mCurrentDrawing_DocumentOpenRequested;
                    mCurrentDrawing.Canvas = null;
                }
                canvas1.Children.Clear();
                PropertyBrowser.Children.Clear();
                mCurrentDrawing = value;
                if (mCurrentDrawing != null)
                {
                    mCurrentDrawing.Canvas = canvas1;
                    mCurrentDrawing.ActionManager.CollectionChanged += ActionManager_CollectionChanged;
                    mCurrentDrawing.ConstructionStepComplete += mCurrentDrawing_ConstructionStepComplete;
                    mCurrentDrawing.Status += mCurrentDrawing_Status;
                    mCurrentDrawing.SelectionChanged += mCurrentDrawing_SelectionChanged;
                    mCurrentDrawing.BehaviorChanged += mCurrentDrawing_BehaviorChanged;
                    mCurrentDrawing.DocumentOpenRequested += mCurrentDrawing_DocumentOpenRequested;
                    mCurrentDrawing.SetDefaultBehavior();
                    mCurrentDrawing.CoordinateGrid.Visible = ShowCoordinateGrid;
                }
                UpdateUndoRedo();
            }
        }

        void mCurrentDrawing_DocumentOpenRequested(object sender, Drawing.DocumentOpenRequestedEventArgs e)
        {
            LoadDrawing(e.DocumentXml);
        }

        void mCurrentDrawing_BehaviorChanged(Behavior newBehavior)
        {
            var button = GetButtonForBehavior(CurrentDrawing.Behavior);
            SelectedButton = button;
            var help = CurrentDrawing.Behavior.HintText;
            if (!help.IsEmpty())
            {
                ShowHint(help);
            }
            ShowProperties(CurrentDrawing.Behavior.PropertyBag);
        }

        void mCurrentDrawing_SelectionChanged(object sender, Drawing.SelectionChangedEventArgs e)
        {
            var selection = e.SelectedFigures
                    .Where(f => f != null && f.Visible)
                    .FirstOrDefault();
            ShowProperties(selection);
        }

        object PropertyBrowserContents;

        void ShowProperties(object selection)
        {
            PropertyBrowserContents = selection;
            PropertyGrid.FillControls(PropertyBrowser, selection);
        }

        void mCurrentDrawing_Status(string obj)
        {
            ShowHint(obj);
        }

        void mCurrentDrawing_ConstructionStepComplete(object sender, Drawing.ConstructionStepCompleteEventArgs args)
        {
            if (args.ConstructionComplete)
            {
                UpdateUndoRedo();
                HideHint();
            }
            else
            {
                UndoButton.IsEnabled = false;
                RedoButton.IsEnabled = false;
                string expectedFigure = "";
                if (args.FigureTypeNeeded.HasInterface<IPoint>())
                {
                    expectedFigure = "point";
                }
                else if (args.FigureTypeNeeded.HasInterface<ILine>())
                {
                    expectedFigure = "line, ray or a segment";
                }
                else if (args.FigureTypeNeeded.HasInterface<ICircle>())
                {
                    expectedFigure = "circle";
                }
                else if (args.FigureTypeNeeded.HasInterface<ILinearFigure>())
                {
                    expectedFigure = "line or circle";
                }
                else
                {
                    expectedFigure = args.FigureTypeNeeded.Name;
                }
                string hint = string.Format("Select a {0}", expectedFigure);
                ShowHint(hint);
            }
        }

        public void LoadDrawing(string drawingXml)
        {
            var xml = XElement.Parse(drawingXml);
            LoadDrawing(xml);
        }

        public void LoadDrawing(XElement element)
        {
            CurrentDrawing = new Drawing(canvas1);
            CurrentDrawing.AddFromXml(element);
        }

        #endregion

        #region Sample drawings

#if LibraryEnabled

        public static string LoadStringResource(string fileName)
        {
            var stream = Application.GetResourceStream(
                new Uri("SilverlightDG;component/" + fileName,
                    UriKind.Relative));
            using (StreamReader s = new StreamReader(stream.Stream))
            {
                return s.ReadToEnd();
            }
        }

        public DrawingStorage SampleDrawings { get; set; }

        private void LoadSampleDrawings()
        {
            var text = LoadStringResource("data/drawings.xml");
            SampleDrawings = new DrawingStorage(text);
            foreach (var drawing in SampleDrawings.Drawings)
            {
                var item = new ListBoxItem();
                item.Content = drawing.Attribute("Name").Value;
                item.Tag = drawing;
                item.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(item_MouseLeftButtonUp);
                ServerDrawingList.Items.Add(item);
            }
        }

        void item_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var xml = (sender as ListBoxItem).Tag as XElement;
            if (xml != null)
            {
                LoadDrawing(xml);
            }
        }

        private void ViewLibrary_Click(object sender, RoutedEventArgs e)
        {
            if (ServerDrawingPanel.Visibility == Visibility.Collapsed)
            {
                ServerDrawingPanel.Visibility = Visibility.Visible;
            }
            else
            {
                ServerDrawingPanel.Visibility = Visibility.Collapsed;
            }
        }

#endif

        #endregion

        #region Geometry Toolbar

        RadioButton selectedButton = null;
        Brush selectedBackground = (Brush)FindResource("ToolButtonFillChecked");
        public RadioButton SelectedButton
        {
            get
            {
                return selectedButton;
            }
            set
            {
                if (selectedButton != null)
                {
                    selectedButton.Background = GetBackground(selectedButton.Tag as Behavior);
                    selectedButton.Padding = new Thickness(2, 2, 2, 2);
                    selectedButton.BorderThickness = new Thickness(1, 1, 0, 0);
                    selectedButton.BorderBrush = new SolidColorBrush(Colors.White);
                }
                selectedButton = value;
                if (selectedButton != null)
                {
                    if (!(selectedButton.IsChecked ?? false))
                    {
                        selectedButton.IsChecked = true;
                    }
                    selectedButton.Background = selectedBackground;
                    selectedButton.Padding = new Thickness(3, 3, 2, 1);
                    selectedButton.BorderThickness = new Thickness(0);
                    selectedButton.BorderBrush = new SolidColorBrush(Colors.Transparent);
                }
            }
        }

        private void InitializeToolbar()
        {
            foreach (var behavior in Behavior.Singletons)
            {
                var button = new RadioButton();
                StackPanel stackPanel = new StackPanel();
                var icon = behavior.CreateIcon();
                icon.HorizontalAlignment = HorizontalAlignment.Center;
                stackPanel.Children.Add(icon);
                stackPanel.Children.Add(new TextBlock()
                {
                    Text = behavior.Name,
                    HorizontalAlignment = HorizontalAlignment.Center
                });
                button.Content = stackPanel;
                button.Click += Button_Click;
                button.Tag = behavior;

                button.Style = (Style)FindResource("ToolButtonStyle");
                toolbar.Children.Add(button);

                button.Background = GetBackground(behavior);
                button.Padding = new Thickness(2, 2, 2, 2);
                button.BorderThickness = new Thickness(0, 0, 1, 1);
                button.BorderBrush = new SolidColorBrush(Colors.White);
                //ToolTipService.SetToolTip(button, behavior.Name);
            }
            ToolbarOrientation = Orientation.Horizontal;
            UpdateToolbarButtonTextVisibility();
        }

        private bool mShowToolbarButtonText = false;
        public bool ShowToolbarButtonText
        {
            get
            {
                return mShowToolbarButtonText;
            }
            set
            {
                mShowToolbarButtonText = value;
                UpdateToolbarButtonTextVisibility();
            }
        }

        private bool mShowCoordinateGrid = false;
        public bool ShowCoordinateGrid
        {
            get
            {
                return mShowCoordinateGrid;
            }
            set
            {
                mShowCoordinateGrid = value;
                CurrentDrawing.CoordinateGrid.Visible = value;
            }
        }

        void UpdateToolbarButtonTextVisibility()
        {
            var show = ShowToolbarButtonText;
            foreach (var item in ToolbarButtons())
            {
                item.MinWidth = show ? 50 : 10;
                var stackPanel = item.Content as StackPanel;
                if (stackPanel != null && stackPanel.Children.Count == 2)
                {
                    var text = stackPanel.Children[1] as TextBlock;
                    if (text != null)
                    {
                        text.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
                    }
                }
            }
        }

        private void AdjustSpacingBetweenToolbarButtons()
        {
            foreach (var button in ToolbarButtons())
            {
                var behavior = button.Tag;
                if (behavior is SegmentCreator
                    || behavior is CircleCreator
                    || behavior is MidpointCreator
                    || behavior is PolygonCreator
                    || behavior is CoordinatesMeasurementCreator)
                {
                    if (toolbar.Orientation == Orientation.Horizontal)
                    {
                        button.Margin = new Thickness(8, 0, 0, 0);
                    }
                    else
                    {
                        button.Margin = new Thickness(0, 8, 0, 0);
                    }
                }
                else
                {
                    button.Margin = new Thickness(0, 0, 0, 0);
                }
            }
        }

        private IEnumerable<RadioButton> ToolbarButtons()
        {
            return toolbar.Children.Where(c => c is RadioButton).Select(c => c as RadioButton);
        }

        private void ToggleToolbarOrientation_Click(object sender, RoutedEventArgs e)
        {
            ToolbarOrientation = ToolbarOrientation == Orientation.Horizontal
                ? Orientation.Vertical
                : Orientation.Horizontal;
        }

        Orientation ToolbarOrientation
        {
            get
            {
                return toolbar.Orientation;
            }
            set
            {
                toolbar.Orientation = value;
                AdjustSpacingBetweenToolbarButtons();
            }
        }

        static Dictionary<Behavior, LinearGradientBrush> brushes = new Dictionary<Behavior, LinearGradientBrush>();
        static Brush GetBackground(Behavior behavior)
        {
            if (brushes.ContainsKey(behavior))
            {
                return brushes[behavior];
            }
            Color gradientColor = behavior.ToolButtonColor;
            LinearGradientBrush brush = new LinearGradientBrush();
            brush.StartPoint = new Point(0, 0);
            brush.EndPoint = new Point(1, 1);
            brush.GradientStops.Add(new GradientStop() { Offset = 0.0, Color = Colors.White });
            brush.GradientStops.Add(new GradientStop() { Offset = 0.6, Color = gradientColor });
            brush.GradientStops.Add(new GradientStop() { Offset = 1.2, Color = Colors.White });
            brushes.Add(behavior, brush);
            return brush;
        }

        public static object FindResource(string name)
        {
            if (App.Current.Resources.Contains(name))
            {
                return App.Current.Resources[name];
            }
            else
            {
                FrameworkElement root = App.Current.RootVisual as FrameworkElement;
                return root.FindResource(name);
            }
        }

        private void ShowToolbarText_Checked(object sender, RoutedEventArgs e)
        {
            ShowToolbarButtonText = !ShowToolbarButtonText;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var b = (sender as ButtonBase).Tag as Behavior;
            if (b != null)
            {
                CurrentDrawing.Behavior = b;
            }
        }

        RadioButton GetButtonForBehavior(Behavior behavior)
        {
            return ToolbarButtons().Where(b => b.Tag == behavior).FirstOrDefault();
        }

        #endregion

        #region Undo/Redo

        private void UndoButton_Click(object sender, RoutedEventArgs e)
        {
            CurrentDrawing.ActionManager.Undo();
            UpdateUndoRedo();
        }

        private void RedoButton_Click(object sender, RoutedEventArgs e)
        {
            CurrentDrawing.ActionManager.Redo();
            UpdateUndoRedo();
        }

        private void UpdateUndoRedo()
        {
            UndoButton.IsEnabled = CurrentDrawing.ActionManager.CanUndo; // ? Visibility.Visible : Visibility.Collapsed;
            RedoButton.IsEnabled = CurrentDrawing.ActionManager.CanRedo; // ? Visibility.Visible : Visibility.Collapsed;
            ClearButton.IsEnabled = CurrentDrawing.Figures.Count > 0;
            HideHint();
        }

        void ActionManager_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            UpdateUndoRedo();
        }

        #endregion

        #region UI Controls

        private void ClearButton_Click(object sender, RoutedEventArgs e)
        {
            CurrentDrawing = new Drawing(canvas1);
        }

        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            if (TextPane.Visibility == Visibility.Visible)
            {
                TextPane.Visibility = Visibility.Collapsed;
                return;
            }

            textOutput.Text = CurrentDrawing.SaveAsText();
            TextPane.Visibility = Visibility.Visible;
        }

        private void CloseTextPane_Click(object sender, RoutedEventArgs e)
        {
            SaveButton.IsChecked = false;
            TextPane.Visibility = Visibility.Collapsed;
        }

        private void RefreshXml_Click(object sender, RoutedEventArgs e)
        {
            textOutput.Text = CurrentDrawing.SaveAsText();
        }

        public void ShowHint(string text)
        {
            if (text.IsEmpty())
            {
                HintPanel.Visibility = Visibility.Collapsed;
            }
            else
            {
                HintPanelText.Text = text;
                HintPanel.Visibility = Visibility.Visible;
            }
        }

        public void HideHint()
        {
            HintPanel.Visibility = Visibility.Collapsed;
        }

        #endregion

        #region Settings

        Settings PageSettings;

        public class Settings
        {
            public Settings(Page page)
            {
                Page = page;
            }

            Page Page;

            [PropertyGridVisible]
            [PropertyGridName("Show coordinate axes and grid")]
            public bool ShowGrid
            {
                get
                {
                    return Page.ShowCoordinateGrid;
                }
                set
                {
                    Page.ShowCoordinateGrid = value;
                }
            }

            [PropertyGridVisible]
            [PropertyGridName("Show text in toolbar buttons")]
            public bool ShowToolbarText
            {
                get
                {
                    return Page.ShowToolbarButtonText;
                }
                set
                {
                    Page.ShowToolbarButtonText = value;
                }
            }

            [PropertyGridVisible]
            [PropertyGridName("Toolbar orientation")]
            public Orientation ToolbarOrientation
            {
                get
                {
                    return Page.ToolbarOrientation;
                }
                set
                {
                    Page.ToolbarOrientation = value;
                }
            }
        }

        private void SettingsButton_Click(object sender, RoutedEventArgs e)
        {
            if (PropertyBrowserContents == PageSettings)
            {
                ShowProperties(null);
            }
            else
            {
                ShowProperties(PageSettings);
            }
        }

        #endregion
    }
}
